using System.Text;
using Microsoft.AspNetCore.Http;
using Vin.Event;
using Vin.Extension.ServerSentEvent.EventArgs;
using Vin.Extension.ServerSentEvent.Model;
using Vin.Extension.ServerSentEvent.Options;
using Vin.Tool.AspNetCore.Service;
using Vin.Tool.AspNetCore.Singleton;
using Vin.Tool.Core.JsonCategory;

namespace Vin.Extension.ServerSentEvent.Client;

public abstract class SseClient : VinBaseService, ISseClientAdapter, IDisposable
{
    protected bool _isConnected = true;

    public string Id { get; protected set; }

    public bool IsConnected => !HttpContext.RequestAborted.IsCancellationRequested && _isConnected;

    protected HttpRequest Request => HttpContext.Request;
    
    protected HttpResponse Response => HttpContext.Response;

    protected readonly Queue<SseMsg> _messageQueue = new();

    public SseClient()
    {
        Id = Guid.NewGuid().ToString("N");

        var sseOption = VinApp.GetOption<SseOption>();

        Response.ContentType = "text/event-stream";
        Response.Headers.Append("Cache-Control", "no-cache");
        Response.Headers.Append("Connection", "keep-alive");

        // 允许跨域
        var origin = HttpContext.Request.Headers.Origin;
        if (origin.Count > 0 && sseOption.CorsOrigins.Contains(origin.ToString()))
        {
            Response.Headers.Append("Access-Control-Allow-Origin", origin);
            // Response.Headers.Append("Access-Control-Allow-Origin", "http://tauri.localhost");
        }

        VinEventManager.Register<SseClientMessageEventArg>(MessageEventHandler);
    }

    #region 默认的推送消息

    public async Task MessageEventHandler(object? sender, VinEventArgs e)
    {
        if (e is not SseClientMessageEventArg sseClientMessageEventArg)
        {
            return;
        }

        if (sseClientMessageEventArg.ClientId == Id.ToString())
        {
            await PushMessageAsync(sseClientMessageEventArg.Message);
        }
    }

    /// <summary>
    /// 推送消息
    /// </summary>
    public async Task PushMessageAsync<T>(SseMsg<T> message)
    {
        _messageQueue.Enqueue(new SseMsg(message.Data.ToCamelCaseJson(), message.Event, message.Id, message.Retry));
        await Task.CompletedTask;
    }

    /// <summary>
    /// 推送消息
    /// </summary>
    public Task PushMessageAsync(SseMsg message)
    {
        _messageQueue.Enqueue(message);
        return Task.CompletedTask;
    }

    /// <summary>
    /// 推送消息
    /// </summary>
    /// <param name="data">消息</param>
    /// <param name="eventName">事件名称</param>
    /// <param name="id">id</param>
    /// <param name="retry">重试</param>
    public async Task PushMessageAsync(string data, string? eventName = null, string? id = null, string? retry = null)
    {
        _messageQueue.Enqueue(new SseMsg(data, eventName, id, retry));
        await Task.CompletedTask;
    }

    #endregion

    #region 处理消息

    /// <summary>
    /// 默认连接消息
    /// </summary>
    public async Task DefaultConnectMessageAsync()
    {
        await SendAsync(Id.ToString(), "connected");
    }

    /// <summary>
    /// 处理连接
    /// </summary>
    public virtual async Task HandleAsync()
    {
        await DefaultConnectMessageAsync();

        await HandleMessageAsync();

        await DefaultDisconnectMessageAsync();
    }

    /// <summary>
    /// 处理消息队列
    /// </summary>
    public async Task HandleMessageAsync()
    {
        while (IsConnected)
        {
            if (_messageQueue.Count > 0)
            {
                var msg = _messageQueue.Dequeue();
                await SendAsync(msg);
            }
        }
    }

    /// <summary>
    /// 默认断开连接消息
    /// </summary>
    public async Task DefaultDisconnectMessageAsync()
    {
        await SendAsync(Id.ToString(), "disconnected");
    }

    public Task DisconnectAsync()
    {
        _isConnected = false;
        return Task.CompletedTask;
    }

    #endregion

    #region 发送消息

    public async Task SendAsync(string data, string? @event = null, string? id = null, string? retry = null)
    {
        if (!_isConnected)
        {
            Console.WriteLine("Client is disconnected.");
            return;
        }

        if (!string.IsNullOrEmpty(id))
        {
            await Response.WriteAsync($"id: {id}\n");
        }

        if (!string.IsNullOrEmpty(@event))
        {
            await Response.WriteAsync($"event: {@event}\n");
        }

        if (!string.IsNullOrEmpty(retry))
        {
            await Response.WriteAsync($"retry: {retry}\n");
        }

        await Response.WriteAsync($"data: {data}\n\n", Encoding.UTF8);
        await Response.Body.FlushAsync();
    }

    public async Task SendAsync(object data, string? @event = null, string? id = null, string? retry = null)
    {
        await SendAsync(data.ToCamelCaseJson(), @event, id, retry);
    }

    public async Task SendAsync<T>(T data, string? @event = null, string? id = null, string? retry = null)
    {
        await SendAsync(data.ToCamelCaseJson(), @event, id, retry);
    }

    public async Task SendAsync<T>(SseMsg<T> msg)
    {
        await SendAsync(msg.Data, msg.Event, msg.Id, msg.Retry);
    }

    public async Task SendAsync(SseMsg msg)
    {
        await SendAsync(msg.Data ?? "", msg.Event, msg.Id, msg.Retry);
    }

    #endregion

    public void Dispose()
    {
        VinEventManager.UnRegister<SseClientMessageEventArg>(MessageEventHandler);
    }
}